home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1b1 Distribution / WASTE Source / WESelecting.p < prev    next >
Text File  |  1995-06-01  |  46KB  |  1,651 lines

  1. unit WESelecting;
  2.  
  3. { WASTE PROJECT }
  4. { Drawing Selections, Activating, Updating, Scrolling, etc. }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WELineLayout;
  12.  
  13.     function WEGetOffset (thePoint: LongPt;
  14.                                     var edge: SignedByte;
  15.                                     hWE: WEHandle): LongInt;
  16.     procedure WEGetPoint (offset: LongInt;
  17.                                     var thePoint: LongPt;
  18.                                     var lineHeight: Integer;
  19.                                     hWE: WEHandle);
  20.     function WEGetHiliteRgn (rangeStart, rangeEnd: LongInt;
  21.                                     hWE: WEHandle): RgnHandle;
  22.     procedure _WEDrawCaret (offset: LongInt;
  23.                                     hWE: WEHandle);
  24.     procedure _WEBlinkCaret (hWE: WEHandle);
  25.     procedure _WEHiliteRange (rangeStart, rangeEnd: LongInt;
  26.                                     hWE: WEHandle);
  27.     procedure WEUpdate (updateRgn: RgnHandle;
  28.                                     hWE: WEHandle);
  29.     procedure WEScroll (hOffset, vOffset: LongInt;
  30.                                     hWE: WEHandle);
  31.     function _WEScrollIntoView (offset: LongInt;
  32.                                     hWE: WEHandle): Boolean;
  33.     procedure WESelView (hWE: WEHandle);
  34.     procedure WEActivate (hWE: WEHandle);
  35.     procedure WEDeactivate (hWE: WEHandle);
  36.     function WEIsActive (hWE: WEHandle): Boolean;
  37.     procedure _WEDoArrowKey (arrow: Integer;
  38.                                     modifiers: Integer;
  39.                                     hWE: WEHandle);
  40.     function WEAdjustCursor (mouseLoc: Point;
  41.                                     mouseRgn: RgnHandle;
  42.                                     hWE: WEHandle): Boolean;
  43.     procedure WEIdle (var maxSleep: LongInt;
  44.                                     hWE: WEHandle);
  45.     procedure WESetSelection (selStart, selEnd: LongInt;
  46.                                     hWE: WEHandle);
  47.     procedure WESetAlignment (alignment: SignedByte;
  48.                                     hWE: WEHandle);
  49.     procedure WEFindWord (offset: LongInt;
  50.                                     edge: SignedByte;
  51.                                     var wordStart, wordEnd: LongInt;
  52.                                     hWE: WEHandle);
  53.     procedure WEFindLine (offset: LongInt;
  54.                                     edge: SignedByte;
  55.                                     var lineStart, lineEnd: LongInt;
  56.                                     hWE: WEHandle);
  57.     function WECharByte (offset: LongInt;
  58.                                     hWE: WEHandle): Integer;
  59.     function WECharType (offset: LongInt;
  60.                                     hWE: WEHandle): Integer;
  61.  
  62. implementation
  63.     uses
  64.         LowMem, QDOffscreen, ToolUtils;
  65.  
  66. { If WASTE_OBJECTS_ARE_GLYPHS is TRUE, WEGetOffset treats embedded objects }
  67. { like ordinary glyphs and never returns kObjectEdge in the edge parameter: }
  68. { as a result, clicking in the middle of an object always positions the caret either }
  69. { to the left or to the right of the object. }
  70.  
  71. { If WASTE_OBJECTS_ARE_GLYPHS is FALSE, WEGetOffset returns kObjectEdge in }
  72. { the edge parameter when thePoint is in the middle half of an object: as a result, }
  73. { clicking in the middle of an object immediately _selects_ the object, so that a second }
  74. { click immediately following triggers the 'clik' callback (this can be handy to make }
  75. { sound objects play when they are double-clicked. }
  76.  
  77. {$SETC WASTE_OBJECTS_ARE_GLYPHS = FALSE}
  78.  
  79.     const
  80.  
  81. { values for _WEArrowOffset action parameter: }
  82. { plain arrow keys }
  83.         kGoLeft = 0;
  84.         kGoRight = 1;
  85.         kGoUp = 2;
  86.         kGoDown = 3;
  87.  
  88. { modifiers }
  89.         kOption = 4;
  90.         kCommand = 8;
  91.  
  92. { option + arrow combos }
  93.         kGoWordStart = kGoLeft + kOption;
  94.         kGoWordEnd = kGoRight + kOption;
  95.         kGoTextStart = kGoUp + kOption;
  96.         kGoTextEnd = kGoDown + kOption;
  97.  
  98. { command + arrow combos }
  99.         kGoLineStart = kGoLeft + kCommand;
  100.         kGoLineEnd = kGoRight + kCommand;
  101.         kGoPageStart = kGoUp + kCommand;
  102.         kGoPageEnd = kGoDown + kCommand;
  103.  
  104. {$IFC GENERATINGCFM}
  105.  
  106.     procedure _WEClearHiliteBit;
  107.     begin
  108.         LMSetHiliteMode(BAND(LMGetHiliteMode, $7F));
  109.     end;  { _WEClearHiliteBit }
  110.  
  111. {$ELSEC}
  112.  
  113.     procedure _WEClearHiliteBit;
  114.     inline
  115.         $08B8, $0007, $0938;         { bclr #7, HiliteMode }
  116.  
  117. {$ENDC}
  118.  
  119.     function WEGetOffset (thePoint: LongPt;
  120.                                     var edge: SignedByte;
  121.                                     hWE: WEHandle): LongInt;
  122.  
  123. { given a long point in local coordinates, }
  124. { find the text offset corresponding to the nearest glyph }
  125.  
  126.         var
  127.             pWE: WEPtr;
  128.             lineIndex: LongInt;
  129.             hPos: Fixed;
  130.             pixelWidth: Fixed;
  131.             saveWELock: Boolean;
  132.  
  133.         function SLPixelToChar (pLine: LinePtr;
  134.                                         pAttrs: WERunAttributesPtr;
  135.                                         pSegment: Ptr;
  136.                                         segmentStart, segmentLength: LongInt;
  137.                                         styleRunPosition: JustStyleCode): Boolean;
  138.             var
  139.                 slop, objectWidth, subWidth, oldWidth: Fixed;
  140.                 cType: Integer;
  141.         begin
  142.             SLPixelToChar := false;
  143.  
  144. { if this is the first style run on the line, subtract pen indent from pixelWidth }
  145.             if (styleRunPosition <= leftStyleRun) then
  146.                 pixelWidth := pixelWidth - BSL(_WECalcPenIndent(pLine^.lineSlop, pWE^.alignment), 16);
  147.  
  148. { if pixelWidth is gone negative already, the point is on the trailing edge of first glyph }
  149.             if (pixelWidth < 0) then
  150.                 begin
  151.                     WEGetOffset := segmentStart;
  152.                     edge := kTrailingEdge;
  153.                     SLPixelToChar := true;
  154.                     Exit(SLPixelToChar);
  155.                 end;
  156.  
  157.             oldWidth := pixelWidth;
  158.  
  159.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  160.                 begin
  161.  
  162. { EMBEDDED OBJECT }
  163. { calculate object width as Fixed }
  164.                     objectWidth := BSL(WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h, 16);
  165.  
  166. { subtract object width from pixelWidth }
  167.                     pixelWidth := pixelWidth - objectWidth;
  168.  
  169. {$IFC WASTE_OBJECTS_ARE_GLYPHS}
  170.  
  171. { find out whether the point is in the leftmost half of the object, }
  172. { in the rightmost half or past the object }
  173.                     subWidth := BSR(objectWidth, 1);                    { divide by two }
  174.                     if (pixelWidth + subWidth < 0) then
  175.                         begin
  176.                             WEGetOffset := segmentStart;
  177.                             edge := kLeadingEdge;                    { point is in leftmost half of object }
  178.                         end
  179.                     else
  180.                         begin
  181.                             WEGetOffset := segmentStart + 1;
  182.                             if (pixelWidth < 0) then
  183.                                 edge := kTrailingEdge                    { point is in rightmost half of object }
  184.                             else
  185.                                 edge := kLeadingEdge;                    { point is past object }
  186.                         end;
  187.  
  188. {$ELSEC}
  189.  
  190. { find out whether the point is in the leftmost quarter of the object, }
  191. { in the middle half, in the rightmost quarter or past the object }
  192.                     subWidth := BSR(objectWidth, 2);                    { divide by four }
  193.                     if (pixelWidth + subWidth < 0) then
  194.                         begin
  195.                             WEGetOffset := segmentStart;
  196.                             if (pixelWidth + objectWidth < subWidth) then
  197.                                 edge := kLeadingEdge                    { point is in leftmost quarter of object }
  198.                             else
  199.                                 edge := kObjectEdge;                        { point is in middle half of object }
  200.                         end
  201.                     else
  202.                         begin
  203.                             WEGetOffset := segmentStart + 1;
  204.                             if (pixelWidth < 0) then
  205.                                 edge := kTrailingEdge                    { point is in rightmost quarter of object }
  206.                             else
  207.                                 edge := kLeadingEdge;                    { point is past object }
  208.                         end;
  209.  
  210. {$ENDC}
  211.  
  212.                 end  { EMBEDDED OBJECT }
  213.             else
  214.                 begin
  215.  
  216. { REGULAR TEXT }
  217. { if this is the last segment on the line, strip the last blank character (if any), }
  218. { unless it is the last non-CR character in the whole text stream }
  219.                     if (not Odd(styleRunPosition)) then
  220.                         if (segmentStart + segmentLength < pWE^.textLength) | (Ptr(LongInt(pSegment) + segmentLength - 1)^ = kEOL) then
  221.                             begin
  222.                                 cType := CallWECharTypeProc(pSegment, segmentLength - 1, FontScript, hWE, pWE^.charTypeHook);
  223.                                 if (BAND(cType, smcTypeMask + smcClassMask) = smCharPunct + smPunctBlank) then
  224.                                     if (BAND(cType, smcDoubleMask) = 0) then
  225.                                         segmentLength := segmentLength - 1
  226.                                     else
  227.                                         segmentLength := segmentLength - 2;
  228.                             end;
  229.  
  230. { calculate slop for this text segment (justified text only) }
  231.                     if (pWE^.alignment = weJustify) then
  232.                         slop := FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling)), pLine^.lineJustAmount)
  233.                     else
  234.                         slop := 0;
  235.  
  236. {$IFC WASTE_DEBUG}
  237.                     _WEAssert(pWE^.pixelToCharHook <> nil, 'Missing PixelToChar Hook');
  238. {$ENDC}
  239.  
  240. { call PixelToChar hook for this segment }
  241.                     WEGetOffset := segmentStart + CallWEPixelToCharProc(pSegment, segmentLength, slop, pixelWidth, edge, styleRunPosition, hPos, hWE, pWE^.pixelToCharHook);
  242.  
  243.                 end;  { REGULAR TEXT }
  244.  
  245. { increment hPos by change in pixelWidth }
  246.             hPos := hPos + (oldWidth - pixelWidth);
  247.  
  248. { if pixelWidth has gone negative, we're finished; otherwise go to next run }
  249.             if (pixelWidth < 0) then
  250.                 SLPixelToChar := true;
  251.  
  252.         end;  { SLPixelToChar }
  253.  
  254.     begin
  255.  
  256. { lock the WE record }
  257.         saveWELock := _WESetHandleLock(hWE, true);
  258.         pWE := hWE^;
  259.  
  260. { offset thePoint so that it is relative to the top left corner of the destination rectangle }
  261.         thePoint.v := thePoint.v - pWE^.destRect.top;
  262.         thePoint.h := thePoint.h - pWE^.destRect.left;
  263.  
  264. { if the point is above the destination rect, return zero }
  265.         if (thePoint.v < 0) then
  266.             begin
  267.                 WEGetOffset := 0;
  268.                 edge := kTrailingEdge;
  269.             end
  270.         else
  271.  
  272. { if the point is below the last line, return last char offset }
  273.             if (thePoint.v >= WEGetHeight(0, maxLongInt, hWE)) then
  274.                 begin
  275.                     WEGetOffset := pWE^.textLength;
  276.                     edge := kLeadingEdge;
  277.                 end
  278.             else
  279.                 begin
  280.  
  281. { find the line index corresponding to the vertical pixel offset }
  282.                     lineIndex := _WEPixelToLine(thePoint.v, hWE);
  283.  
  284. { express the horizontal pixel offset as a Fixed value }
  285.                     pixelWidth := BSL(thePoint.h, 16);
  286.                     hPos := 0;
  287.  
  288. { walk through the segments on this line calling PixelToChar }
  289.                     _WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, hWE);
  290.  
  291.                 end;  { else }
  292.  
  293. { unlock the WE record }
  294.         if (_WESetHandleLock(hWE, saveWELock)) then
  295.             ;
  296.  
  297.     end;  { WEGetOffset }
  298.  
  299.     procedure WEGetPoint (offset: LongInt;
  300.                                     var thePoint: LongPt;
  301.                                     var lineHeight: Integer;
  302.                                     hWE: WEHandle);
  303.  
  304. { given a byte offset into the text, find the corresponding glyph position }
  305. { this routine is useful for highlighting the text and for positioning the caret }
  306.  
  307.         var
  308.             pWE: WEPtr;
  309.             pLine: LinePeek;
  310.             lineIndex: LongInt;
  311.             saveWELock: Boolean;
  312.  
  313.         function SLCharToPixel (pLine: LinePtr;
  314.                                         pAttrs: WERunAttributesPtr;
  315.                                         pSegment: Ptr;
  316.                                         segmentStart, segmentLength: LongInt;
  317.                                         styleRunPosition: JustStyleCode): Boolean;
  318.             var
  319.                 slop: Fixed;
  320.                 width: Integer;
  321.                 isInSegment: Boolean;
  322.         begin
  323.  
  324. { is offset within this segment? }
  325.             isInSegment := (offset < segmentStart + segmentLength);
  326.  
  327. { if this is the first style run on the line, add pen indent to thePoint.h }
  328.             if (styleRunPosition <= leftStyleRun) then
  329.                 thePoint.h := thePoint.h + _WECalcPenIndent(pLine^.lineSlop, pWE^.alignment);
  330.  
  331.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  332.                 begin
  333.  
  334. { EMBEDDED OBJECT }
  335.                     if (isInSegment) then
  336.                         width := 0
  337.                     else
  338.                         width := WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h;
  339.  
  340.                 end
  341.             else
  342.                 begin
  343.  
  344. { REGULAR TEXT }
  345. { calculate slop for this text segment (justified text only) }
  346.                     if (pWE^.alignment = weJustify) then
  347.                         slop := FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling)), pLine^.lineJustAmount)
  348.                     else
  349.                         slop := 0;
  350.  
  351. {$IFC WASTE_DEBUG}
  352.                     _WEAssert(pWE^.charToPixelHook <> nil, 'Missing CharToPixel Hook');
  353. {$ENDC}
  354.  
  355. { call CharToPixel hook to get width of segment up to specified offset }
  356.                     width := CallWECharToPixelProc(pSegment, segmentLength, slop, offset - segmentStart, hilite, styleRunPosition, thePoint.h, hWE, pWE^.charToPixelHook);
  357.  
  358.                 end;
  359.  
  360. { advance thePoint.h by the width of this segment }
  361.             thePoint.h := thePoint.h + width;
  362.  
  363. { drop out of loop when we reach offset }
  364.             SLCharToPixel := isInSegment;
  365.  
  366.         end;  { SLCharToPixel }
  367.  
  368.     begin
  369.  
  370. { lock the WE record }
  371.         saveWELock := _WESetHandleLock(hWE, true);
  372.         pWE := hWE^;
  373.  
  374. { the base point is the top left corner of the destination rectangle }
  375.         thePoint := pWE^.destRect.topLeft;
  376.  
  377. { first of all find the line on which the glyph lies }
  378.         lineIndex := WEOffsetToLine(offset, hWE);
  379.  
  380. { calculate the vertical coordinate and the line height }
  381.         pLine := @pWE^.hLines^^[lineIndex];
  382.         thePoint.v := thePoint.v + pLine^.first.lineOrigin;
  383.         lineHeight := pLine^.second.lineOrigin - pLine^.first.lineOrigin;
  384.  
  385.         if ((offset = pWE^.textLength) & (WEGetChar(offset - 1, hWE) = CHR(kEOL))) then
  386.             begin
  387.  
  388. { SPECIAL CASE: if offset is past the last character and }
  389. { the last character is a carriage return, return a point below the last line }
  390.  
  391.                 thePoint.v := thePoint.v + lineHeight;
  392.                 thePoint.h := thePoint.h + _WECalcPenIndent(pWE^.destRect.right - pWE^.destRect.left, pWE^.alignment);
  393.             end
  394.         else
  395.  
  396. { to get the horizontal coordinate, walk through the style runs on this line }
  397.             _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, hWE);
  398.  
  399. { pin the horizontal coordinate to the destination rectangle }
  400.         thePoint.h := _WEPinInRange(thePoint.h, pWE^.destRect.left, pWE^.destRect.right);
  401.  
  402. { unlock the WE record }
  403.         if (_WESetHandleLock(hWE, saveWELock)) then
  404.             ;
  405.  
  406.     end;  { WEGetPoint }
  407.  
  408.     procedure WEFindLine (offset: LongInt;
  409.                                     edge: SignedByte;
  410.                                     var lineStart, lineEnd: LongInt;
  411.                                     hWE: WEHandle);
  412.         var
  413.             pWE: WEPtr;
  414.             pLine: LineArrayPtr;
  415.     begin
  416.         pWE := hWE^;
  417.         pLine := @pWE^.hLines^^[WEOffsetToLine(offset, hWE)];
  418.         lineStart := pLine^[0].lineStart;
  419.         lineEnd := pLine^[1].lineStart;
  420.     end;  { WEFindLine }
  421.  
  422.     function _WEGetContext (offset: LongInt;
  423.                                     var contextStart, contextEnd: LongInt;
  424.                                     hWE: WEHandle): ScriptCode;
  425.  
  426. { This function finds a range of characters ("context"), all belonging to the same script }
  427. { and centered around the specified offset. }
  428. { The function result is the ID of a font belonging to this script. }
  429. { Ideally, the context should consist of a whole script run, but in practice the returned }
  430. { context can be narrower, for performance and other reasons (see below) }
  431.  
  432.         var
  433.             index, saveIndex, saveRunEnd: LongInt;
  434.             runInfo: WERunInfo;
  435.             script1, script2: ScriptCode;
  436.     begin
  437.         if BTST(hWE^^.flags, weFNonRoman) then
  438.             begin
  439.  
  440. { if more than one script is installed, limit the search of script run boundaries }
  441. { to a single line, for speed's sake }
  442.                 WEFindLine(offset, kLeadingEdge, contextStart, contextEnd, hWE);
  443.  
  444. { find the style run the specified offset is in }
  445.                 index := _WEOffsetToRun(offset, hWE);
  446.                 _WEGetIndStyle(index, runInfo, hWE);
  447.  
  448. { find the script code associated with this style run }
  449.                 script1 := FontToScript(runInfo.runAttrs.runStyle.tsFont);
  450.  
  451. { the script code is returned as function result }
  452.                 _WEGetContext := script1;
  453.  
  454. { save index and runInfo.runEnd for the second while loop }
  455.                 saveIndex := index;
  456.                 saveRunEnd := runInfo.runEnd;
  457.  
  458. { walk backwards across style runs preceding offset, looking for a script run boundary }
  459.                 while (runInfo.runStart > contextStart) do
  460.                     begin
  461.                         index := index - 1;
  462.                         _WEGetIndStyle(index, runInfo, hWE);
  463.                         script2 := FontToScript(runInfo.runAttrs.runStyle.tsFont);
  464.                         if (script1 <> script2) then
  465.                             begin
  466.                                 contextStart := runInfo.runEnd;
  467.                                 Leave;
  468.                             end;
  469.                     end;  { while }
  470.  
  471. { restore index and runInfo.runEnd }
  472.                 index := saveIndex;
  473.                 runInfo.runEnd := saveRunEnd;
  474.  
  475. { walk forward across style runs following offset, looking for a script run boundary }
  476.                 while (runInfo.runEnd < contextEnd) do
  477.                     begin
  478.                         index := index + 1;
  479.                         _WEGetIndStyle(index, runInfo, hWE);
  480.                         script2 := FontToScript(runInfo.runAttrs.runStyle.tsFont);
  481.                         if (script1 <> script2) then
  482.                             begin
  483.                                 contextEnd := runInfo.runStart;
  484.                                 Leave;
  485.                             end;
  486.                     end;  { while }
  487.  
  488.             end
  489.         else
  490.             begin
  491.  
  492. { only the Roman script is enabled: the whole text constitutes one script run }
  493.                 _WEGetContext := smRoman;
  494.                 contextStart := 0;
  495.                 contextEnd := hWE^^.textLength;
  496.             end;
  497.  
  498. { make sure the range identified by contextStart/contextEnd is contained within }
  499. { the 32K byte range centered around the specified offset }
  500. { the reason for this is that many Script Manager routines (e.g. FindWord and CharByte) }
  501. { only accept 16-bit offsets, rather than 32-bit offsets }
  502.  
  503.         contextStart := _WEPinInRange(contextStart, offset - (maxint div 2), offset);
  504.         contextEnd := _WEPinInRange(contextEnd, offset, offset + (maxint div 2));
  505.  
  506.     end;  { _WEGetContext }
  507.  
  508.     function _WEGetRestrictedContext (offset: LongInt;
  509.                                     var contextStart, contextEnd: LongInt;
  510.                                     hWE: WEHandle): ScriptCode;
  511.  
  512. { This function finds a range of characters ("context"), all belonging to the same script }
  513. { and centered around the specified offset. }
  514. { This function returns a script run subrange and is more efficient than }
  515. { _WEGetContext because it doesn't try to find the script boundaries accurately. }
  516.  
  517.         var
  518.             runInfo: WERunInfo;
  519.     begin
  520.  
  521. { just find the style run the specified offset is in }
  522.         WEGetRunInfo(offset, runInfo, hWE);
  523.         contextStart := runInfo.runStart;
  524.         contextEnd := runInfo.runEnd;
  525.         _WEGetRestrictedContext := FontToScript(runInfo.runAttrs.runStyle.tsFont);
  526.  
  527. { make sure the range identified by contextStart/contextEnd is contained within }
  528. { the 32K byte range centered around the specified offset }
  529. { the reason for this is that many Script Manager routines (e.g. FindWord and CharByte) }
  530. { only accept 16-bit offsets, rather than 32-bit offsets }
  531.  
  532.         contextStart := _WEPinInRange(contextStart, offset - (maxint div 2), offset);
  533.         contextEnd := _WEPinInRange(contextEnd, offset, offset + (maxint div 2));
  534.  
  535.     end;  { _WEGetRestrictedContext }
  536.  
  537.     procedure WEFindWord (offset: LongInt;
  538.                                     edge: SignedByte;
  539.                                     var wordStart, wordEnd: LongInt;
  540.                                     hWE: WEHandle);
  541.         var
  542.             pWE: WEPtr;
  543.             script: ScriptCode;
  544.             contextStart, contextEnd: LongInt;
  545.             hText: Handle;
  546.             wordBreaks: OffsetTable;
  547.             saveWELock, saveTextLock: Boolean;
  548.     begin
  549.  
  550. { lock the WE record }
  551.         saveWELock := _WESetHandleLock(hWE, true);
  552.         pWE := hWE^;
  553.  
  554. { find a script context containing the specified offset }
  555. { (words cannot straddle script boundaries) }
  556.         script := _WEGetContext(offset, contextStart, contextEnd, hWE);
  557.  
  558. { lock the text }
  559.         hText := pWE^.hText;
  560.         saveTextLock := _WESetHandleLock(hText, true);
  561.  
  562. { call the word break hook }
  563.         CallWEWordBreakProc(Ptr(LongInt(hText^) + contextStart), contextEnd - contextStart, offset - contextStart, edge, wordBreaks, script, hWE, pWE^.wordBreakHook);
  564.  
  565. { unlock the text }
  566.         if (_WESetHandleLock(hText, saveTextLock)) then
  567.             ;
  568.  
  569. { calculate wordStart and wordEnd relative to the beginning of the text }
  570.         wordStart := contextStart + wordBreaks[0].offFirst;
  571.         wordEnd := contextStart + wordBreaks[0].offSecond;
  572.  
  573. { unlock the WE record }
  574.         if (_WESetHandleLock(hWE, saveWELock)) then
  575.             ;
  576.  
  577.     end;  { WEFindWord }
  578.  
  579.     function WECharByte (offset: LongInt;
  580.                                     hWE: WEHandle): Integer;
  581.         var
  582.             pWE: WEPtr;
  583.             script: ScriptCode;
  584.             contextStart, contextEnd: LongInt;
  585.             hText: Handle;
  586.             saveWELock, saveTextLock: Boolean;
  587.     begin
  588.         WECharByte := smSingleByte;
  589.  
  590. { lock the WE record }
  591.         saveWELock := _WESetHandleLock(hWE, true);
  592.         pWE := hWE^;
  593.  
  594. { do nothing unless there is at least one double-byte script system installed }
  595. { and make sure offset is within allowed bounds }
  596.         if (BTST(pWE^.flags, weFDoubleByte)) then
  597.             if ((offset >= 0) and (offset < pWE^.textLength)) then
  598.                 begin
  599.  
  600. { find a script context containing the specified offset }
  601.                     script := _WEGetRestrictedContext(offset, contextStart, contextEnd, hWE);
  602.  
  603. { lock the text }
  604.                     hText := pWE^.hText;
  605.                     saveTextLock := _WESetHandleLock(hText, true);
  606.  
  607. { pass the CharByte hook a pointer to the beginning of the style run }
  608.                     WECharByte := CallWECharByteProc(Ptr(LongInt(hText^) + contextStart), offset - contextStart, script, hWE, pWE^.charByteHook);
  609.  
  610. { unlock the text }
  611.                     if (_WESetHandleLock(hText, saveTextLock)) then
  612.                         ;
  613.  
  614.                 end;
  615.  
  616. { unlock the WE record }
  617.         if (_WESetHandleLock(hWE, saveWELock)) then
  618.             ;
  619.  
  620.     end;  { WECharByte }
  621.  
  622.     function WECharType (offset: LongInt;
  623.                                     hWE: WEHandle): Integer;
  624.         var
  625.             pWE: WEPtr;
  626.             script: ScriptCode;
  627.             contextStart, contextEnd: LongInt;
  628.             hText: Handle;
  629.             saveWELock, saveTextLock: Boolean;
  630.     begin
  631.         WECharType := 0;
  632.  
  633. { lock the WE record }
  634.         saveWELock := _WESetHandleLock(hWE, true);
  635.         pWE := hWE^;
  636.  
  637. { make sure offset is within allowed bounds }
  638.         if ((offset >= 0) and (offset < pWE^.textLength)) then
  639.             begin
  640.  
  641. { find a script context containing the specified offset }
  642.                 script := _WEGetRestrictedContext(offset, contextStart, contextEnd, hWE);
  643.  
  644. { lock the text }
  645.                 hText := pWE^.hText;
  646.                 saveTextLock := _WESetHandleLock(hText, true);
  647.  
  648. { pass the CharType hook a pointer to the beginning of the style run }
  649.                 WECharType := CallWECharTypeProc(Ptr(LongInt(hText^) + contextStart), offset - contextStart, script, hWE, pWE^.charTypeHook);
  650.  
  651. { unlock the text }
  652.                 if (_WESetHandleLock(hText, saveTextLock)) then
  653.                     ;
  654.  
  655.             end;
  656.  
  657. { unlock the WE record }
  658.         if (_WESetHandleLock(hWE, saveWELock)) then
  659.             ;
  660.  
  661.     end;  { WECharType }
  662.  
  663.     procedure _WEDrawCaret (offset: LongInt;
  664.                                     hWE: WEHandle);
  665.         var
  666.             pWE: WEPtr;
  667.             thePoint: LongPt;
  668.             caretRect: Rect;
  669.             caretHeight: Integer;
  670.             savePort: GrafPtr;
  671.             saveClip: RgnHandle;
  672.     begin
  673.  
  674. { the WE record must be already locked }
  675.         pWE := hWE^;
  676.  
  677. { find the caret position using WEGetPoint }
  678.         WEGetPoint(offset, thePoint, caretHeight, hWE);
  679.         WELongPointToPoint(thePoint, caretRect.topLeft);
  680.         if (caretRect.left > pWE^.destRect.left) then
  681.             caretRect.left := caretRect.left - 1;
  682.  
  683. { calculate caret rectangle }
  684.         caretRect.bottom := caretRect.top + caretHeight;
  685.         caretRect.right := caretRect.left + kCaretWidth;
  686.  
  687. { set up the port }
  688.         GetPort(savePort);
  689.         SetPort(pWE^.port);
  690.  
  691. { clip to the view region }
  692.         saveClip := NewRgn;
  693.         GetClip(saveClip);
  694.         SetClip(pWE^.viewRgn);
  695.  
  696. { draw the caret }
  697.         InvertRect(caretRect);
  698.  
  699. { restore the clip region }
  700.         SetClip(saveClip);
  701.         DisposeRgn(saveClip);
  702.  
  703. { restore the port }
  704.         SetPort(savePort);
  705.  
  706.     end;  { _WEDrawCaret }
  707.  
  708.     procedure _WEBlinkCaret (hWE: WEHandle);
  709.         var
  710.             pWE: WEPtr;
  711.     begin
  712.  
  713. { the WE record must be already locked }
  714.         pWE := hWE^;
  715.  
  716. { do nothing if we're not active }
  717.         if (not BTST(pWE^.flags, weFActive)) then
  718.             Exit(_WEBlinkCaret);
  719.  
  720. { redraw the caret, in XOR mode }
  721.         _WEDrawCaret(pWE^.selStart, hWE);
  722.  
  723. { keep track of the current caret visibility status }
  724.         pWE^.flags := BXOR(pWE^.flags, BSL(1, weFCaretVisible));
  725.  
  726. { update caretTime }
  727.         pWE^.caretTime := TickCount;
  728.  
  729.     end;  { _WEBlinkCaret }
  730.  
  731.     function WEGetHiliteRgn (rangeStart, rangeEnd: LongInt;
  732.                                     hWE: WEHandle): RgnHandle;
  733.  
  734. { returns the hilite region corresponding to the specified range }
  735. { the caller is responsible for disposing of the returned region }
  736. { when it's finished with it }
  737.  
  738.         var
  739.             pWE: WEPtr;
  740.             hiliteRgn: RgnHandle;
  741.             selRect: LongRect;
  742.             firstPoint, lastPoint: LongPt;
  743.             firstLineHeight, lastLineHeight: Integer;
  744.             r: Rect;
  745.             savePort: GrafPtr;
  746.             saveWELock: Boolean;
  747.     begin
  748.  
  749. { lock the WE record }
  750.         saveWELock := _WESetHandleLock(hWE, true);
  751.         pWE := hWE^;
  752.  
  753. { set up the port }
  754.         GetPort(savePort);
  755.         SetPort(pWE^.port);
  756.  
  757. { make sure rangeStart comes before rangeEnd }
  758.         _WEReorder(rangeStart, rangeEnd);
  759.  
  760. { calculate pixel location corresponding to rangeStart }
  761.         WEGetPoint(rangeStart, firstPoint, firstLineHeight, hWE);
  762.  
  763. { calculate pixel location corresponding to rangeEnd }
  764.         WEGetPoint(rangeEnd, lastPoint, lastLineHeight, hWE);
  765.  
  766. { open a region: rects to be hilited will be accumulated in this }
  767.         OpenRgn;
  768.  
  769.         if (firstPoint.v = lastPoint.v) then
  770.             begin
  771.  
  772. { selection range encompasses only one line }
  773.                 WESetLongRect(selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  774.                 WELongRectToRect(selRect, r);
  775.                 FrameRect(r);
  776.             end
  777.         else
  778.             begin
  779.  
  780. { selection range encompasses more than one line }
  781. { hilite the first line }
  782.                 WESetLongRect(selRect, firstPoint.h, firstPoint.v, pWE^.destRect.right, firstPoint.v + firstLineHeight);
  783.                 WELongRectToRect(selRect, r);
  784.                 FrameRect(r);
  785.  
  786. { any lines between the first and the last one? }
  787.                 if (firstPoint.v + firstLineHeight < lastPoint.v) then
  788.                     begin
  789.  
  790. { hilite all the lines in-between }
  791.                         WESetLongRect(selRect, pWE^.destRect.left, firstPoint.v + firstLineHeight, pWE^.destRect.right, lastPoint.v);
  792.                         WELongRectToRect(selRect, r);
  793.                         FrameRect(r);
  794.                     end;
  795.  
  796. { hilite the last line }
  797.                 WESetLongRect(selRect, pWE^.destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  798.                 WELongRectToRect(selRect, r);
  799.                 FrameRect(r);
  800.             end;
  801.  
  802. { copy the accumulated region into a new region }
  803.         hiliteRgn := NewRgn;
  804.         CloseRgn(hiliteRgn);
  805.  
  806. { restrict this region to the view region }
  807.         SectRgn(hiliteRgn, pWE^.viewRgn, hiliteRgn);
  808.  
  809. { restore the port }
  810.         SetPort(savePort);
  811.  
  812. { unlock the WE record }
  813.         if (_WESetHandleLock(hWE, saveWELock)) then
  814.             ;
  815.  
  816. { return the hilite region }
  817.         WEGetHiliteRgn := hiliteRgn;
  818.  
  819.     end;  { WEGetHiliteRgn }
  820.  
  821.     procedure _WEHiliteRange (rangeStart, rangeEnd: LongInt;
  822.                                     hWE: WEHandle);
  823.         var
  824.             pWE: WEPtr;
  825.             saveClip, auxRgn, hiliteRgn: RgnHandle;
  826.             savePen: PenState;
  827.             savePort: GrafPtr;
  828.     begin
  829.  
  830. { the WE record must be already locked }
  831.         pWE := hWE^;
  832.  
  833. { do nothing if the specified range is empty }
  834.         if (rangeStart = rangeEnd) then
  835.             Exit(_WEHiliteRange);
  836.  
  837. { set up the port }
  838.         GetPort(savePort);
  839.         SetPort(pWE^.port);
  840.  
  841. { create auxiliary regions }
  842.         saveClip := NewRgn;
  843.         auxRgn := NewRgn;
  844.  
  845. { restrict the clip region to the view rectangle }
  846.         GetClip(saveClip);
  847.         SectRgn(saveClip, pWE^.viewRgn, auxRgn);
  848.         SetClip(auxRgn);
  849.  
  850. { get the hilite region corresponding to the specified range }
  851.         hiliteRgn := WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
  852.  
  853. { hilite the region or frame it, depending on the setting of the active flag }
  854.         if BTST(pWE^.flags, weFActive) then
  855.             begin
  856.                 _WEClearHiliteBit;
  857.                 InvertRgn(hiliteRgn);
  858.             end
  859.         else if BTST(pWE^.flags, weFOutlineHilite) then
  860.             begin
  861.                 GetPenState(savePen);
  862.                 PenNormal;
  863.                 PenMode(patXor);
  864.                 _WEClearHiliteBit;
  865.                 FrameRgn(hiliteRgn);
  866.                 SetPenState(savePen);
  867.             end;
  868.  
  869. { restore the clip region }
  870.         SetClip(saveClip);
  871.  
  872. { dispose of all regions }
  873.         DisposeRgn(saveClip);
  874.         DisposeRgn(auxRgn);
  875.         DisposeRgn(hiliteRgn);
  876.  
  877. { restore the port }
  878.         SetPort(savePort);
  879.  
  880.     end;  { _WEHiliteRange }
  881.  
  882.     procedure WESetSelection (selStart, selEnd: LongInt;
  883.                                     hWE: WEHandle);
  884.         var
  885.             pWE: WEPtr;
  886.             oldSelStart, oldSelEnd: LongInt;
  887.             saveWELock: Boolean;
  888.     begin
  889.  
  890. { lock the WE record }
  891.         saveWELock := _WESetHandleLock(hWE, true);
  892.         pWE := hWE^;
  893.  
  894. { range-check parameters }
  895.         selStart := _WEPinInRange(selStart, 0, pWE^.textLength);
  896.         selEnd := _WEPinInRange(selEnd, 0, pWE^.textLength);
  897.  
  898. { set the weFAnchorIsEnd bit if selStart > selEnd, then reorder the endpoints }
  899.         if (selStart > selEnd) then
  900.             BSET(pWE^.flags, weFAnchorIsEnd)
  901.         else
  902.             BCLR(pWE^.flags, weFAnchorIsEnd);
  903.         _WEReorder(selStart, selEnd);
  904.  
  905. { get old selection range }
  906.         oldSelStart := pWE^.selStart;
  907.         oldSelEnd := pWE^.selEnd;
  908.  
  909. { selection changed? }
  910.         if ((oldSelStart <> selStart) or (oldSelEnd <> selEnd)) then
  911.             begin
  912.  
  913. { invalid the null style }
  914.                 BCLR(pWE^.flags, weFUseNullStyle);
  915.  
  916. { hide the caret if it's showing }
  917.                 if BTST(pWE^.flags, weFCaretVisible) then
  918.                     _WEBlinkCaret(hWE);
  919.  
  920. { set new selection range }
  921.                 pWE^.selStart := selStart;
  922.                 pWE^.selEnd := selEnd;
  923.  
  924. { skip this section if redrawing has been inhibited }
  925.                 if (not BTST(pWE^.flags, weFInhibitRecal)) then
  926.                     begin
  927.  
  928. { if we're active, invert the exclusive-OR between the old range and the new range. }
  929. { if we're inactive, this optimization can't be used because of outline highlighting. }
  930.                         if BTST(pWE^.flags, weFActive) then
  931.                             begin
  932.                                 _WEReorder(oldSelStart, selStart);
  933.                                 _WEReorder(oldSelEnd, selEnd);
  934.                                 _WEReorder(oldSelEnd, selStart);
  935.                             end;
  936.  
  937.                         _WEHiliteRange(oldSelStart, oldSelEnd, hWE);
  938.                         _WEHiliteRange(selStart, selEnd, hWE);
  939.  
  940.                         if (not BTST(pWE^.flags, weFMouseTracking)) then
  941.                             begin
  942.  
  943. { redraw the caret immediately, if the selection range is empty }
  944.                                 if (pWE^.selStart = pWE^.selEnd) then
  945.                                     _WEBlinkCaret(hWE);
  946.  
  947. { clear clickCount, unless we're tracking the mouse }
  948.                                 pWE^.clickCount := 0;
  949.  
  950. { scroll the selection into view, unless we're tracking the mouse }
  951.                                 WESelView(hWE);
  952.  
  953.                             end;
  954.                     end;  { if redrawing not inhibited }
  955.             end;  { if selection changed }
  956.  
  957. { unlock the WE record }
  958.         if (_WESetHandleLock(hWE, saveWELock)) then
  959.             ;
  960.  
  961.     end;  { WESetSelection }
  962.  
  963.     procedure WESetAlignment (alignment: SignedByte;
  964.                                     hWE: WEHandle);
  965.         var
  966.             pWE: WEPtr;
  967.             oldAlignment: Integer;
  968.             saveWELock: Boolean;
  969.     begin
  970.  
  971. { lock the WE record }
  972.         saveWELock := _WESetHandleLock(hWE, true);
  973.         pWE := hWE^;
  974.         oldAlignment := pWE^.alignment;
  975.  
  976.         if ((alignment >= weFlushLeft) and (alignment <= weJustify) and (alignment <> oldAlignment)) then
  977.             begin
  978.  
  979. { hide the caret if it's showing }
  980.                 if (BTST(pWE^.flags, weFCaretVisible)) then
  981.                     _WEBlinkCaret(hWE);
  982.  
  983. { change the alignment }
  984.                 pWE^.alignment := alignment;
  985.  
  986. { if the text was left-aligned, then we haven't been bothering till now, }
  987. { so we have to recalc the whole document }
  988.                 if (oldAlignment = weFlushLeft) then
  989.                     _WERecalSlops(0, pWE^.nLines - 1, hWE);
  990.  
  991. { redraw the view rectangle }
  992.                 WEUpdate(nil, hWE);
  993.             end;
  994.  
  995. { unlock the WE record }
  996.         if (_WESetHandleLock(hWE, saveWELock)) then
  997.             ;
  998.     end;  { WESetAlignment }
  999.  
  1000.     function _WEArrowOffset (action: Integer;
  1001.                                     offset: LongInt;
  1002.                                     hWE: WEHandle): LongInt;
  1003.  
  1004. { given an action code (corresponding to a modifiers + arrow key combo) }
  1005. { and an offset into the text, find the offset of the new caret position }
  1006.  
  1007.         var
  1008.             thePoint: LongPt;
  1009.             textLength, rangeStart, rangeEnd: LongInt;
  1010.             lineHeight: Integer;
  1011.             edge: SignedByte;
  1012.     begin
  1013.         textLength := hWE^^.textLength;
  1014.         case action of
  1015.  
  1016.             kGoLeft: 
  1017.                 if (offset > 0) then
  1018.                     begin
  1019.                         offset := offset - 1;
  1020.                         if (WECharByte(offset, hWE) <> smSingleByte) then
  1021.                             offset := offset - 1;
  1022.                     end;
  1023.  
  1024.             kGoRight: 
  1025.                 if (offset < textLength) then
  1026.                     begin
  1027.                         if (WECharByte(offset, hWE) <> smSingleByte) then
  1028.                             offset := offset + 1;
  1029.                         offset := offset + 1;
  1030.                     end;
  1031.  
  1032.             kGoUp: 
  1033.                 begin
  1034.                     WEGetPoint(offset, thePoint, lineHeight, hWE);
  1035.                     thePoint.v := thePoint.v - 1;
  1036.                     offset := WEGetOffset(thePoint, edge, hWE);
  1037.                 end;
  1038.  
  1039.             kGoDown: 
  1040.                 begin
  1041.                     WEGetPoint(offset, thePoint, lineHeight, hWE);
  1042.                     thePoint.v := thePoint.v + lineHeight;
  1043.                     offset := WEGetOffset(thePoint, edge, hWE);
  1044.                 end;
  1045.  
  1046.             kGoWordStart: 
  1047.                 begin
  1048.                     WEFindWord(offset, kTrailingEdge, rangeStart, rangeEnd, hWE);
  1049.                     offset := rangeStart;
  1050.                 end;
  1051.  
  1052.             kGoWordEnd: 
  1053.                 begin
  1054.                     WEFindWord(offset, kLeadingEdge, rangeStart, rangeEnd, hWE);
  1055.                     offset := rangeEnd;
  1056.                 end;
  1057.  
  1058.             kGoTextStart: 
  1059.                 offset := 0;
  1060.  
  1061.             kGoTextEnd: 
  1062.                 offset := textLength;
  1063.  
  1064.             kGoLineStart: 
  1065.                 begin
  1066.                     WEFindLine(offset, kLeadingEdge, rangeStart, rangeEnd, hWE);
  1067.                     offset := rangeStart;
  1068.                 end;
  1069.  
  1070.             kGoLineEnd: 
  1071.                 begin
  1072.                     WEFindLine(offset, kTrailingEdge, rangeStart, rangeEnd, hWE);
  1073.                     offset := rangeEnd;
  1074.                     if (offset < textLength) then
  1075.                         begin
  1076.                             offset := offset - 1;
  1077.                             if (WECharByte(offset, hWE) <> smSingleByte) then
  1078.                                 offset := offset - 1;
  1079.                         end;
  1080.                 end;
  1081.  
  1082.             otherwise
  1083.                 ;
  1084.         end;  { case action }
  1085.  
  1086.         _WEArrowOffset := offset;
  1087.  
  1088.     end;  { _WEArrowOffset }
  1089.  
  1090.     procedure _WEDoArrowKey (arrow: Integer;
  1091.                                     modifiers: Integer;
  1092.                                     hWE: WEHandle);
  1093.  
  1094. { this routine is called by WEKey to handle arrow keys }
  1095. { the WE record is guaranteed to be already locked }
  1096.  
  1097.         var
  1098.             pWE: WEPtr;
  1099.             action: Integer;
  1100.             selStart, selEnd: LongInt;
  1101.             caretLoc, anchor: LongInt;
  1102.     begin
  1103.         pWE := hWE^;
  1104.  
  1105. { calculate the "action" parameter for _WEArrowOffset from arrow and modifiers }
  1106.         action := arrow - kArrowLeft;            { possible range: 0..3 }
  1107.         if (BAND(modifiers, optionKey) <> 0) then
  1108.             action := action + kOption;
  1109.         if (BAND(modifiers, cmdKey) <> 0) then
  1110.             action := action + kCommand;
  1111.  
  1112. { get selection range }
  1113.         selStart := pWE^.selStart;
  1114.         selEnd := pWE^.selEnd;
  1115.  
  1116.         if (BAND(modifiers, shiftKey) = 0) then
  1117.             begin
  1118.  
  1119. { if selection range isn't empty, collapse it to one of the endpoints }
  1120.                 if (selStart < selEnd) then
  1121.                     if ((arrow = kArrowLeft) or (arrow = kArrowUp)) then
  1122.                         caretLoc := selStart
  1123.                     else
  1124.                         caretLoc := selEnd
  1125.                 else
  1126.  
  1127. { otherwise move the insertion point }
  1128.                     caretLoc := _WEArrowOffset(action, selStart, hWE);
  1129.  
  1130. { set anchor to caretLoc, so new selection will be empty }
  1131.                 anchor := caretLoc;
  1132.  
  1133.             end
  1134.         else
  1135.             begin
  1136.  
  1137. { shift key was held down: extend the selection rather than replacing it }
  1138. { find out which selection boundary is the anchor and which is the free endpoint }
  1139.                 if BTST(pWE^.flags, weFAnchorIsEnd) then
  1140.                     begin
  1141.                         anchor := selEnd;
  1142.                         caretLoc := selStart;
  1143.                     end
  1144.                 else
  1145.                     begin
  1146.                         anchor := selStart;
  1147.                         caretLoc := selEnd;
  1148.                     end;
  1149.  
  1150. { move the free endpoint }
  1151.                 caretLoc := _WEArrowOffset(action, caretLoc, hWE);
  1152.  
  1153.             end;
  1154.  
  1155. { select the new selection }
  1156.         WESetSelection(anchor, caretLoc, hWE);
  1157.  
  1158.     end;  { _WEDoArrowKey }
  1159.  
  1160.     function WEAdjustCursor (mouseLoc: Point;
  1161.                                     mouseRgn: RgnHandle;
  1162.                                     hWE: WEHandle): Boolean;
  1163.  
  1164. { Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle. }
  1165. { MouseRgn should be either a valid region handle or NIL. }
  1166. { If mouseRgn is supplied (i.e., if it's not NIL), it is intersected with a region }
  1167. { in global coordinates within which the cursor is to retain its shape. }
  1168. { WEAdjustCursor returns TRUE if the cursor has been set. }
  1169. { Your application should set the cursor only if WEAdjustCursor returns FALSE. }
  1170.  
  1171.         var
  1172.             pWE: WEPtr;
  1173.             auxRgn, hiliteRgn: RgnHandle;
  1174.             cursorType: (kIBeam, kArrow);
  1175.             portDelta: Point;
  1176.             savePort: GrafPtr;
  1177.             saveWELock: Boolean;
  1178.     begin
  1179.         WEAdjustCursor := false;
  1180.         cursorType := kIBeam;
  1181.  
  1182. { lock the WE record }
  1183.         saveWELock := _WESetHandleLock(hWE, true);
  1184.         pWE := hWE^;
  1185.  
  1186. { set up the port }
  1187.         GetPort(savePort);
  1188.         SetPort(pWE^.port);
  1189.  
  1190. { calculate delta between the local coordinate system and the global one }
  1191.         portDelta := Point(0);
  1192.         LocalToGlobal(portDelta);
  1193.  
  1194. { calculate the visible portion of the view rectangle, in global coordinates }
  1195.         auxRgn := NewRgn;
  1196.         CopyRgn(pWE^.viewRgn, auxRgn);
  1197.         SectRgn(auxRgn, pWE^.port^.visRgn, auxRgn);
  1198.         OffsetRgn(auxRgn, portDelta.h, portDelta.v);
  1199.  
  1200.         if (PtInRgn(mouseLoc, auxRgn)) then
  1201.             begin
  1202.  
  1203. { mouse is within view rectangle: it's up to us to set the cursor }
  1204.                 WEAdjustCursor := true;
  1205.  
  1206. { if drag-and-drop is enabled, see if the mouse is within current selection }
  1207.                 if (BTST(pWE^.flags, weFHasDragManager) and BTST(pWE^.flags, weFDragAndDrop)) then
  1208.                     if (pWE^.selStart < pWE^.selEnd) then
  1209.                         begin
  1210.  
  1211. { get current hilite region in global coordinates }
  1212.                             hiliteRgn := WEGetHiliteRgn(pWE^.selStart, pWE^.selEnd, hWE);
  1213.                             OffsetRgn(hiliteRgn, portDelta.h, portDelta.v);
  1214.  
  1215. { if mouse is within selection, set cursor to an arrow, else to an I-beam }
  1216. { (actually, we still use an I-beam if less than DoubleTime ticks have elapsed }
  1217. { since the last mouse click, so that the cursor doesn't turn into an arrow while }
  1218. { triple-clicking + dragging a range of lines) }
  1219.  
  1220.                             if (PtInRgn(mouseLoc, hiliteRgn) and ((TickCount > pWE^.clickTime + GetDblTime) or (pWE^.clickEdge = kObjectEdge))) then
  1221.                                 begin
  1222.                                     cursorType := kArrow;                { use arrow cursor }
  1223.                                     CopyRgn(hiliteRgn, auxRgn);
  1224.                                 end
  1225.                             else
  1226.                                 DiffRgn(auxRgn, hiliteRgn, auxRgn);
  1227.  
  1228. { dispose of the hilite region }
  1229.                             DisposeRgn(hiliteRgn);
  1230.  
  1231.                         end;  { if drag-and-drop is enabled }
  1232.  
  1233. { set the cursor }
  1234.                 if (cursorType = kIBeam) then
  1235.                     SetCursor(GetCursor(iBeamCursor)^^)
  1236.                 else
  1237.                     SetCursor(GetQDGlobalsPtr^.arrow);
  1238.  
  1239. { set mouseRgn, if provided }
  1240.                 if (mouseRgn <> nil) then
  1241.                     SectRgn(mouseRgn, auxRgn, mouseRgn);
  1242.  
  1243.             end
  1244.         else
  1245.  
  1246. { mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn }
  1247.             if (mouseRgn <> nil) then
  1248.                 DiffRgn(mouseRgn, auxRgn, mouseRgn);
  1249.  
  1250. { dispose of the temporary region }
  1251.         DisposeRgn(auxRgn);
  1252.  
  1253. { restore the port }
  1254.         SetPort(savePort);
  1255.  
  1256. { unlock the WE record }
  1257.         if (_WESetHandleLock(hWE, saveWELock)) then
  1258.             ;
  1259.  
  1260.     end;  { WEAdjustCursor }
  1261.  
  1262.     procedure WEIdle (var maxSleep: LongInt;
  1263.                                     hWE: WEHandle);
  1264.         var
  1265.             pWE: WEPtr;
  1266.             caretInterval, sleepTime: LongInt;
  1267.             saveWELock: Boolean;
  1268.     begin
  1269.  
  1270. { lock the WE record }
  1271.         saveWELock := _WESetHandleLock(hWE, true);
  1272.         pWE := hWE^;
  1273.  
  1274. {$IFC WASTE_DEBUG}
  1275.         _WESanityCheck(hWE);
  1276. {$ENDC}
  1277.  
  1278. { the caret blinks only if we're active and the selection point is empty }
  1279.         if (BTST(pWE^.flags, weFActive) and (pWE^.selStart = pWE^.selEnd)) then
  1280.             begin
  1281.  
  1282. { the low-memory global variable CaretTime contains the preferred interval }
  1283. { between successive inversions of the caret }
  1284.                 caretInterval := GetCaretTime;
  1285.  
  1286. { calculate how many ticks we can sleep before we need to invert the caret }
  1287. { the caretTime field of the WE record contains the time of the last inversion }
  1288.                 sleepTime := caretInterval - (TickCount - pWE^.caretTime);
  1289.  
  1290. { if sleepTime has gone negative, invert the caret }
  1291.                 if (sleepTime <= 0) then
  1292.                     begin
  1293.                         _WEBlinkCaret(hWE);
  1294.                         sleepTime := caretInterval;
  1295.                     end;
  1296.  
  1297.             end
  1298.         else
  1299.  
  1300. { if we don't need to blink the caret, we can sleep forever }
  1301.             sleepTime := maxLongInt;
  1302.  
  1303. { return sleepTime to the caller if maxSleep isn't NIL }
  1304.         if (@maxSleep <> nil) then
  1305.             maxSleep := sleepTime;
  1306.  
  1307. { unlock the WE record }
  1308.         if (_WESetHandleLock(hWE, saveWELock)) then
  1309.             ;
  1310.  
  1311.     end;  { WEIdle }
  1312.  
  1313.     procedure WEUpdate (updateRgn: RgnHandle;
  1314.                                     hWE: WEHandle);
  1315.         var
  1316.             pWE: WEPtr;
  1317.             firstLine, lastLine: LongInt;
  1318.             auxRect: Rect;
  1319.             saveClip, auxRgn: RgnHandle;
  1320.             savePort: GrafPtr;
  1321.             saveWELock: Boolean;
  1322.     begin
  1323.  
  1324. { lock the WE record }
  1325.         saveWELock := _WESetHandleLock(hWE, true);
  1326.         pWE := hWE^;
  1327.  
  1328. { set up the port }
  1329.         GetPort(savePort);
  1330.         SetPort(pWE^.port);
  1331.  
  1332. { save the clip region }
  1333.         saveClip := NewRgn;
  1334.         GetClip(saveClip);
  1335.  
  1336. { clip to the insersection between updateRgn and the view rectangle }
  1337. { (updateRgn may be NIL; in this case, just clip to the view rectangle) }
  1338.         auxRgn := NewRgn;
  1339.         if (updateRgn <> nil) then
  1340.             SectRgn(updateRgn, pWE^.viewRgn, auxRgn)
  1341.         else
  1342.             CopyRgn(pWE^.viewRgn, auxRgn);
  1343.         SetClip(auxRgn);
  1344.  
  1345.         if (EmptyRgn(auxRgn) = false) then
  1346.             begin
  1347.  
  1348. { set auxRect to the bounding box of the update region (clipped to the view rectangle) }
  1349.                 auxRect := auxRgn^^.rgnBBox;
  1350.  
  1351. { find out which lines need to be redrawn }
  1352.                 firstLine := _WEPixelToLine(auxRect.top - pWE^.destRect.top, hWE);
  1353.                 lastLine := _WEPixelToLine((auxRect.bottom - 1) - pWE^.destRect.top, hWE);
  1354.  
  1355. { draw them (if updateRgn is NIL, erase each line rectangle before redrawing) }
  1356.                 _WEDrawLines(firstLine, lastLine, (updateRgn = nil), hWE);
  1357.  
  1358. { hilite the selection range or draw the caret (only if active) }
  1359.                 if (pWE^.selStart < pWE^.selEnd) then
  1360.                     _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE)
  1361.                 else if BTST(pWE^.flags, weFCaretVisible) then
  1362.                     begin
  1363.                         _WEBlinkCaret(hWE);
  1364.                         BSET(pWE^.flags, weFCaretVisible);
  1365.                     end;
  1366.  
  1367.             end;  { if not empty }
  1368.  
  1369.         DisposeRgn(auxRgn);
  1370.  
  1371. { restore the clip region }
  1372.         SetClip(saveClip);
  1373.         DisposeRgn(saveClip);
  1374.  
  1375. { restore the port }
  1376.         SetPort(savePort);
  1377.  
  1378. { unlock the WE record }
  1379.         if (_WESetHandleLock(hWE, saveWELock)) then
  1380.             ;
  1381.  
  1382.     end;  { WEUpdate }
  1383.  
  1384.     procedure WEDeactivate (hWE: WEHandle);
  1385.         var
  1386.             pWE: WEPtr;
  1387.             saveWELock: Boolean;
  1388.     begin
  1389.  
  1390. { lock the WE record }
  1391.         saveWELock := _WESetHandleLock(hWE, true);
  1392.         pWE := hWE^;
  1393.  
  1394. { do nothing if we are already inactive }
  1395.         if (BTST(pWE^.flags, weFActive)) then
  1396.             begin
  1397.  
  1398. { hide the selection range or the caret }
  1399.                 _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1400.                 if BTST(pWE^.flags, weFCaretVisible) then
  1401.                     _WEBlinkCaret(hWE);
  1402.  
  1403. { clear the active flag }
  1404.                 BCLR(pWE^.flags, weFActive);
  1405.  
  1406. { frame the selection }
  1407.                 _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1408.  
  1409. { dispose of the offscreen graphics world, if any }
  1410.                 if (pWE^.offscreenPort <> nil) then
  1411.                     begin
  1412.                         DisposeGWorld(GWorldPtr(pWE^.offscreenPort));
  1413.                         pWE^.offscreenPort := nil;
  1414.                     end;
  1415.  
  1416. { notify Text Services }
  1417.                 if (pWE^.tsmReference <> nil) then
  1418.                     if (DeactivateTSMDocument(pWE^.tsmReference) <> noErr) then
  1419.                         ;
  1420.  
  1421.             end;
  1422.  
  1423. { unlock the WE record }
  1424.         if (_WESetHandleLock(hWE, saveWELock)) then
  1425.             ;
  1426.  
  1427.     end;  { WEDeactivate }
  1428.  
  1429.     procedure WEActivate (hWE: WEHandle);
  1430.         var
  1431.             pWE: WEPtr;
  1432.             saveWELock: Boolean;
  1433.     begin
  1434.  
  1435. { lock the WE record }
  1436.         saveWELock := _WESetHandleLock(hWE, true);
  1437.         pWE := hWE^;
  1438.  
  1439. { do nothing if we are already active }
  1440.         if (not BTST(pWE^.flags, weFActive)) then
  1441.             begin
  1442.  
  1443. { remove the selection frame }
  1444.                 _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1445.  
  1446. { set the active flag }
  1447.                 BSET(pWE^.flags, weFActive);
  1448.  
  1449. { show the selection range }
  1450.                 _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1451.  
  1452. { notify Text Services }
  1453.                 if (pWE^.tsmReference <> nil) then
  1454.                     if (ActivateTSMDocument(pWE^.tsmReference) <> noErr) then
  1455.                         ;
  1456.  
  1457.             end;
  1458.  
  1459. { unlock the WE record }
  1460.         if (_WESetHandleLock(hWE, saveWELock)) then
  1461.             ;
  1462.  
  1463.     end;  { WEActivate }
  1464.  
  1465.     function WEIsActive (hWE: WEHandle): Boolean;
  1466.     begin
  1467.  
  1468. { return TRUE iff the specified WE instance is currently active }
  1469.         WEIsActive := BTST(hWE^^.flags, weFActive);
  1470.  
  1471.     end;  { WEIsActive }
  1472.  
  1473.     procedure WEScroll (hOffset, vOffset: LongInt;
  1474.                                     hWE: WEHandle);
  1475.         var
  1476.             pWE: WEPtr;
  1477.             viewRect: Rect;
  1478.             updateRgn: RgnHandle;
  1479.             savePort: GrafPtr;
  1480.             hideOutline, saveWELock: Boolean;
  1481.     begin
  1482.  
  1483. { do nothing if both scroll offsets are zero }
  1484.         if ((hOffset = 0) and (vOffset = 0)) then
  1485.             Exit(WEScroll);
  1486.  
  1487. { lock the WE record }
  1488.         saveWELock := _WESetHandleLock(hWE, true);
  1489.         pWE := hWE^;
  1490.  
  1491. { hide the caret if it's showing }
  1492.         if BTST(pWE^.flags, weFCaretVisible) then
  1493.             _WEBlinkCaret(hWE);
  1494.  
  1495. { set up the port }
  1496.         GetPort(savePort);
  1497.         SetPort(pWE^.port);
  1498.  
  1499. { if we're inactive and outline highlighting is on, we have to temporarily }
  1500. { hide the selection outline while scrolling to avoid a cosmetic bug }
  1501.         hideOutline := false;
  1502.         if (not BTST(pWE^.flags, weFActive)) then
  1503.             if (BTST(pWE^.flags, weFOutlineHilite)) then
  1504.                 begin
  1505.                     hideOutline := true;
  1506.                     _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1507.                     BCLR(pWE^.flags, weFOutlineHilite);
  1508.                 end;
  1509.  
  1510. { if we're currently tracking a drag, notify the Drag Manager we're about to scroll }
  1511.         if (pWE^.currentDrag <> kNullDrag) then
  1512.             if (DragPreScroll(pWE^.currentDrag, hOffset, vOffset) <> noErr) then
  1513.                 ;
  1514.  
  1515.         viewRect := pWE^.viewRgn^^.rgnBBox;
  1516.         updateRgn := NewRgn;
  1517.  
  1518. { offset the destination rectangle by the specified amount }
  1519.         WEOffsetLongRect(pWE^.destRect, hOffset, vOffset);
  1520.  
  1521. { scroll the view rectangle }
  1522.         ScrollRect(viewRect, hOffset, vOffset, updateRgn);
  1523.  
  1524. { redraw the exposed region }
  1525.         WEUpdate(updateRgn, hWE);
  1526.         DisposeRgn(updateRgn);
  1527.  
  1528. { notify the Drag Manager }
  1529.         if (pWE^.currentDrag <> kNullDrag) then
  1530.             if (DragPostScroll(pWE^.currentDrag) <> noErr) then
  1531.                 ;
  1532.  
  1533. { redraw the selection outline, if hidden }
  1534.         if (hideOutline) then
  1535.             begin
  1536.                 BSET(pWE^.flags, weFOutlineHilite);
  1537.                 _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  1538.             end;
  1539.  
  1540. { restore the port }
  1541.         SetPort(savePort);
  1542.  
  1543. { unlock the WE record }
  1544.         if (_WESetHandleLock(hWE, saveWELock)) then
  1545.             ;
  1546.  
  1547.     end;  { WEScroll }
  1548.  
  1549.     function _WEScrollIntoView (offset: LongInt;
  1550.                                     hWE: WEHandle): Boolean;
  1551.         var
  1552.             pWE: WEPtr;
  1553.             thePoint: LongPt;
  1554.             lineHeight: Integer;
  1555.             hScroll, vScroll, temp: LongInt;
  1556.     begin
  1557.         pWE := hWE^;
  1558.  
  1559. { do nothing if automatic scrolling is disabled }
  1560.         if (not BTST(pWE^.flags, weFAutoScroll)) then
  1561.             Exit(_WEScrollIntoView);
  1562.  
  1563. { find the selection point }
  1564.         WEGetPoint(offset, thePoint, lineHeight, hWE);
  1565.  
  1566. { assume no scrolling is needed }
  1567.         _WEScrollIntoView := false;
  1568.         vScroll := 0;
  1569.         hScroll := 0;
  1570.  
  1571. { determine if we need to scroll vertically }
  1572.         if (thePoint.v < pWE^.viewRect.top) or (thePoint.v + lineHeight >= pWE^.viewRect.bottom) then
  1573.             begin
  1574.  
  1575. { calculate the amount of vertical scrolling needed to center the selection into view }
  1576.                 vScroll := BSR(pWE^.viewRect.top + pWE^.viewRect.bottom, 1) - thePoint.v;
  1577.  
  1578. { we'd like to superimpose the bottom margins of the dest/view rects, if possible }
  1579.                 temp := pWE^.viewRect.bottom - pWE^.destRect.bottom;
  1580.                 if (temp > vScroll) then
  1581.                     vScroll := temp;
  1582.  
  1583. { but we also have to make sure the dest top isn't scrolled below the view top }
  1584.                 temp := pWE^.viewRect.top - pWE^.destRect.top;
  1585.                 if (temp < vScroll) then
  1586.                     vScroll := temp;
  1587.  
  1588.             end;
  1589.  
  1590. { determine if we need to scroll horizontally }
  1591.         if (thePoint.h - 1 < pWE^.viewRect.left) or (thePoint.h >= pWE^.viewRect.right) then
  1592.             begin
  1593.  
  1594. { calculate the amount of horizontal scrolling needed to center the selection into view }
  1595.                 hScroll := BSR(pWE^.viewRect.left + pWE^.viewRect.right, 1) - thePoint.h;
  1596.  
  1597. { we'd like to superimpose the right margins of the dest/view rects, if possible }
  1598.                 temp := pWE^.viewRect.right - pWE^.destRect.right;
  1599.                 if (temp > hScroll) then
  1600.                     hScroll := temp;
  1601.  
  1602. { but we also have to make sure the dest left isn't scrolled to the right of the view left }
  1603.                 temp := pWE^.viewRect.left - pWE^.destRect.left;
  1604.                 if (temp < hScroll) then
  1605.                     hScroll := temp;
  1606.  
  1607.             end;
  1608.  
  1609. { scroll the text if necessary }
  1610.         if ((vScroll <> 0) or (hScroll <> 0)) then
  1611.             begin
  1612.                 _WEScrollIntoView := true;
  1613.                 WEScroll(hScroll, vScroll, hWE);
  1614.                 BSET(pWE^.flags, weFDestRectChanged);
  1615.             end;
  1616.  
  1617. { notify our client of changes to the destination rectangle }
  1618.         if (BTST(pWE^.flags, weFDestRectChanged)) then
  1619.             begin
  1620.                 if (pWE^.scrollProc <> nil) then
  1621.                     CallWEScrollProc(hWE, pWE^.scrollProc);
  1622.                 BCLR(pWE^.flags, weFDestRectChanged);
  1623.             end;
  1624.     end;  { _WEScrollIntoView }
  1625.  
  1626.     procedure WESelView (hWE: WEHandle);
  1627.         var
  1628.             pWE: WEPtr;
  1629.             offset: LongInt;
  1630.             saveWELock: Boolean;
  1631.     begin
  1632.  
  1633. { lock the WE record }
  1634.         saveWELock := _WESetHandleLock(hWE, true);
  1635.         pWE := hWE^;
  1636.  
  1637. { scroll the free endpoint of the selection into view }
  1638.         if (BTST(pWE^.flags, weFAnchorIsEnd)) then
  1639.             offset := pWE^.selStart
  1640.         else
  1641.             offset := pWE^.selEnd;
  1642.         if (_WEScrollIntoView(offset, hWE)) then
  1643.             ;
  1644.  
  1645. { unlock the WE record }
  1646.         if (_WESetHandleLock(hWE, saveWELock)) then
  1647.             ;
  1648.  
  1649.     end;  { WESelView }
  1650.  
  1651. end.